/**
 * 
 */
package qmlt.learning.decisiontree;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import qmlt.dataset.Instance;

/**
 * A node in the decision tree.
 * 
 * @author quyin
 */
public class Node
{
    /**
     * (for internal nodes only) indicates which attribute is used to branch at this node.
     */
    public int               splittingAttributeIndex;

    /**
     * (for leaf nodes only) indicates what label does this node predict.
     */
    public Object            targetClass;

    /**
     * contains all the instances when training.
     */
    public List<Instance>    instances;

    /**
     * (for internal nodes only) stores branches.
     */
    public Map<Object, Node> children;

    /**
     * stores predicted instances at leaf nodes for pruning. used by predictor
     */
    public List<Instance>    predictedInstances;

    public Node(List<Instance> initInstances)
    {
        instances = new ArrayList<Instance>(initInstances);
        children = new HashMap<Object, Node>();
        predictedInstances = new ArrayList<Instance>();
    }

    public Node()
    {
        this(new ArrayList<Instance>());
    }

    public boolean isLeaf()
    {
        return children.size() == 0;
    }

    public int getSubtreeLeafCount()
    {
        if (isLeaf())
            return 1;
        else
        {
            int n = 0;
            for (Node child : children.values())
            {
                n += child.getSubtreeLeafCount();
            }
            return n;
        }
    }

    public int getSubtreeNodeCount()
    {
        if (isLeaf())
            return 1;
        else
        {
            int n = 0;
            for (Node child : children.values())
            {
                n += child.getSubtreeNodeCount();
            }
            return n + 1;
        }
    }

    @Override
    public String toString()
    {
        return String.format(
                "targetClass:%s, splittingAttributeIndex:%s, #instances:%d, #predicted:%d",
                targetClass, splittingAttributeIndex, instances.size(), predictedInstances.size());
    }
}
